#include "UIConfigResourceManager.h"
#include "UIOSAL.h"
#include "CommonUtil.h"
#include "UILogger.h"
#include <stdlib.h>
#include <stdexcept>
#include <limits.h>
#include <float.h>

UIConfigResourceManager* UIConfigResourceManager::m_pInstance = NULL;

UIConfig::UIConfig()
{
}

UIConfig::~UIConfig()
{
}

std::string* UIConfig::getValue(std::string blockName, std::string parameterName)
{
	if (blockName.empty())
		blockName = BLOCK_GENERAL;

	std::map<std::string, std::map<std::string, std::string> >::iterator itBlock;
	itBlock = m_mapBlockMap.find(uppercase(blockName));
	if (itBlock == m_mapBlockMap.end())
		return NULL;
	
	std::map<std::string, std::string>::iterator itPara;
	itPara = itBlock->second.find(uppercase(parameterName));
	if (itPara == itBlock->second.end())
		return NULL;

	return &(itPara->second);
}

void UIConfig::setValue(std::string blockName, std::string parameterName, std::string value)
{
	if (blockName.empty())
		blockName = BLOCK_GENERAL;
	m_mapBlockMap[uppercase(blockName)][uppercase(parameterName)] = value;
}

void UIConfig::getAllParameters(std::string blockName, std::vector<std::string> &ret)
{
	if (blockName.empty())
		blockName = BLOCK_GENERAL;

	std::map<std::string, std::map<std::string, std::string> >::iterator itBlock;
	itBlock = m_mapBlockMap.find(uppercase(blockName));
	if (itBlock == m_mapBlockMap.end())
		return;
	
	std::map<std::string, std::string>::iterator itPara;
	for(itPara = itBlock->second.begin(); itPara != itBlock->second.end(); ++itPara)
		ret.push_back(itPara->first);
}

UIConfigResourceManager* UIConfigResourceManager::getInstance()
{
	if (!m_pInstance)
		m_pInstance = new UIConfigResourceManager(DEFAULT_MAIN_CONFIG, DEFAULT_USER_CONFIG);
	return m_pInstance;
}

void UIConfigResourceManager::create(const char* mainConfigName, const char* userConfigName)
{
	if (!m_pInstance)
		m_pInstance = new UIConfigResourceManager(mainConfigName, userConfigName);
}

void UIConfigResourceManager::release()
{
	if (m_pInstance)
		delete m_pInstance;
	m_pInstance = NULL;
}

UIConfigResourceManager::UIConfigResourceManager(const char* mainConfigName, const char* userConfigName)
{
	UIFile config;

	getLocalIPAddress(m_localIPAddress);

	// parse main config file
	config.openFile(userConfigName);
	std::string line;
	while (config.getLine(line)) {
		trim(line);
		if (startsWith(line, "//") || line.length() == 0) 
			continue;
		if (line.find("=") != std::string::npos) {
			std::vector<std::string> items;
			splitString(line, "=", items);
			if (items.size() == 2) {
				trim(items[0]);
				trim(items[1]);
				m_userVariables[items[0]] = items[1];
			}
			else {
				UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid user config format: " + line);
			}
		}
		else {
			UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid user config format: " + line);
		}
	}
	config.closeFile();

	// parse main config file
	config.openFile(mainConfigName);
	while (config.getLine(line)) {
		trim(line);
		if (startsWith(line, "//") || line.length() == 0) 
			continue;

		if (line.find("=") != std::string::npos) {
			std::vector<std::string> items;
			splitString(line, "=", items);
			if (items.size() == 2) {
				trim(items[0]);
				trim(items[1]);

				if (m_userVariables.find(items[0]) != m_userVariables.end()) {
					items[1] = m_userVariables[items[0]];
				}
				std::string value = replaceVar(items[1]);

				if (startsWith(value, "<") && endsWith(value, ">")) {
					if (value.find(":") != std::string::npos)
						value = value.substr(1, value.length()-2);
					else 
						value = getDir(mainConfigName) + "/" + value.substr(1, value.length()-2);
				}

				if (startsWith(items[0], "$")) {
					m_variables[uppercase(items[0])] = value;
				}

				if (startsWith(uppercase(items[0]), MODULE_ADDRESS_PREFIX)) {
					UIModuleType type = getModuleType(items[0].substr(15, items[0].length()-15));
					if (isLocalAddress(items[1]))
						m_localModules.push_back(type);
				}

				if (equalsIgnoreCase(items[0], SOCKET_SERVER_ADDRESS)) {
					std::vector<std::string> addressItems;
					splitString(items[1], "@", addressItems);
					if (addressItems.size() == 2) {
						if (isLocalAddress(addressItems[0]))
							m_socketServerAddress = MODULE_ADDRESS_LOCAL;
						else
							m_socketServerAddress = addressItems[0];
						
						try {
							//m_socketServerPort = std::stoi(addressItems[1], 0, 10);
							m_socketServerPort = strtol(addressItems[1].c_str(), 0, 10);
						}
						catch (const std::invalid_argument& ia) {
							UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid socket port: " + addressItems[1]);
							m_socketServerPort = DEFAULT_SOCKET_PORT;
						}
						catch (const std::out_of_range& oor) {
							UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid integer: " + addressItems[1]);
							m_socketServerPort = DEFAULT_SOCKET_PORT;
						}
					}
					else {
						UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid socket address: " + items[1]);
					}
				}
			}
			else {
				UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid config format: " + line);
			}
		}
		else {
			UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid config format: " + line);
		}
	}

	// load local modules
	for (std::vector<UIModuleType>::iterator itModule = m_localModules.begin(); itModule != m_localModules.end(); itModule++)
	{
		UILogger::getInstance()->inf(MODULE_OTHER, "UIConfigResourceManager", "loading Module Config: " + getModuleName(*itModule));
		std::string defaultModuleConfigFile = getDir(mainConfigName) + "/" + getModuleName(*itModule) + "/" + lowercase(getModuleName(*itModule)) + "-default.config";
		loadConfig(*itModule, defaultModuleConfigFile);
	}

	// load system parameters
	UILogger::getInstance()->inf(MODULE_OTHER, "UIConfigResourceManager", "loading General System Config... ");
	std::string defaultModuleConfigFile = getDir(mainConfigName) + "/System/system-default.config";
	loadConfig(MODULE_OTHER, defaultModuleConfigFile);
}

UIConfigResourceManager::~UIConfigResourceManager(void)
{
}

void UIConfigResourceManager::loadConfig(UIModuleType moduleType, std::string configFile) 
{
	UIFile config;
	std::string line;
	std::string curBlock = BLOCK_GENERAL;
	
	UILogger::getInstance()->inf(MODULE_OTHER, "UIConfigResourceManager", "loading config: " + configFile);
	
	config.openFile(configFile.c_str());
	while (config.getLine(line)) {
		trim(line);

		// parse comment
		if (startsWith(line, "//") || line.length() == 0) 
			continue;
		
		// parse block definition
		if (startsWith(line, "[")) {
			size_t pos = line.find("]");
			if (pos != std::string::npos) {
				curBlock = line.substr(1, pos-1);
			}
			else {
				UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid block definition: " + line);
			}
			continue;
		}

		// parse include files
		if (startsWith(line, "#include")) {
			size_t begin = line.find("<");
			size_t end   = line.find_last_of(">");
			if (begin != std::string::npos && end != std::string::npos && begin < end-1) {
				std::string includeConfigPath = replaceVar(line.substr(begin+1, end-begin-1));
				if (includeConfigPath.find(":") != std::string::npos)
					loadConfig(moduleType, includeConfigPath);
				else
					loadConfig(moduleType, getDir(configFile) + "/" + includeConfigPath);
			}
			else {
				UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid include definition: " + line);
			}
		}

		// parse key-value pair
		if (line.find("=") != std::string::npos) {
			std::vector<std::string> items;
			splitString(line, "=", items);
			if (items.size() == 2) {
				trim(items[0]);
				trim(items[1]);
				if (m_userVariables.find(items[0]) != m_userVariables.end()) {
					items[1] = m_userVariables[items[0]];
				}

				std::string value = items[1];

				if (startsWith(value, "<") && endsWith(value, ">")) {
					value = value.substr(1, value.length()-2);
					if (startsWith(lowercase(value), "$public")) {
						value = replaceVar(value);
					}
					else {
						if (value.find(":") == std::string::npos)
							value = getDir(configFile) + "/" + value;
					}
				}
				else {
					value = replaceVar(items[1]);
				}

				m_configs[moduleType].setValue(curBlock, items[0], value);
			}
		}
	}
}

bool UIConfigResourceManager::getStringValue(UIModuleType moduleType, std::string blockName, std::string parameterName, std::string &ret)
{
	std::string* value = getValue(moduleType, blockName, parameterName);
	if (value == 0)
		return false;
	ret = *value;
	return true;
}

bool UIConfigResourceManager::getIntValue(UIModuleType moduleType, std::string blockName, std::string parameterName, int &ret)
{
	std::string* value = getValue(moduleType, blockName, parameterName);
	if (value == 0)
		return false;
	try {
		ret = strtol((*value).c_str(), 0, 10);
	}
	catch (const std::invalid_argument& ia) {
		UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid integer: " + *value);
		return false;
	}
	catch (const std::out_of_range& oor) {
		UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid integer: " + *value);
		return false;
	}
	return true;
}

bool UIConfigResourceManager::getFloatValue(UIModuleType moduleType, std::string blockName, std::string parameterName, float &ret)
{
	std::string* value = getValue(moduleType, blockName, parameterName);
	if (value == 0)
		return false;
	try {
		ret = atof((*value).c_str());
	}
	catch (const std::invalid_argument& ia) {
		UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid float: " + *value);
		return false;
	}
	catch (const std::out_of_range& oor) {
		UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid float: " + *value);
		return false;
	}
	return true;
}

bool UIConfigResourceManager::getBoolValue(UIModuleType moduleType, std::string blockName, std::string parameterName, bool &ret)
{
	std::string* value = getValue(moduleType, blockName, parameterName);
	if (value == 0)
		return false;

	if (equalsIgnoreCase(*value, "true")) {
		ret = true;
		return true;
	}
		
	if (equalsIgnoreCase(*value, "false")) {
		ret = false;
		return true;
	}

	UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid boolean: " + *value);
	return false;
}

bool UIConfigResourceManager::getFileName(UIModuleType moduleType, std::string blockName, std::string parameterName, std::string &ret)
{
	std::string* value = getValue(moduleType, blockName, parameterName);
	if (value == 0)
		return false;
	ret = *value;
	return true;
}

bool UIConfigResourceManager::getStringList(UIModuleType moduleType, std::string blockName, std::string parameterName, std::vector<std::string> &ret)
{
	std::string* value = getValue(moduleType, blockName, parameterName);
	if (value == 0)
		return false;

	std::vector<std::string> items;
	if (parseListValue(value, items) == false)
		return false;
	for (std::vector<std::string>::iterator it=items.begin(); it != items.end(); ++it)
		ret.push_back(*it);
	return true;
}

bool UIConfigResourceManager::getIntList(UIModuleType moduleType, std::string blockName, std::string parameterName, std::vector<int> &ret)
{
	std::string* value = getValue(moduleType, blockName, parameterName);
	if (value == 0)
		return false;
	std::vector<std::string> items;
	if (parseListValue(value, items) == false)
		return false;

	for (std::vector<std::string>::iterator it=items.begin(); it != items.end(); ++it) {
		int iVal = INT_MIN;
		try {
			iVal = strtol((*it).c_str(), 0, 10);
		}
		catch (const std::invalid_argument& ia) {
			UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid integer list: " + *value);
			ret.clear();
			return false;
		}
		catch (const std::out_of_range& oor) {
			UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid integer list: " + *value);
			ret.clear();
			return false;
		}
		ret.push_back(iVal);
	}
	return true;
}

bool UIConfigResourceManager::getFloatList(UIModuleType moduleType, std::string blockName, std::string parameterName, std::vector<float> &ret)
{
	std::string* value = getValue(moduleType, blockName, parameterName);
	if (value == 0)
		return false;
	std::vector<std::string> items;
	if (parseListValue(value, items) == false)
		return false;

	for (std::vector<std::string>::iterator it=items.begin(); it != items.end(); ++it) {
		float fVal = FLT_MIN;
		try {
			fVal = atof((*it).c_str());
		}
		catch (const std::invalid_argument& ia) {
			UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid float list: " + *value);
			ret.clear();
			return false;
		}
		catch (const std::out_of_range& oor) {
			UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid float list: " + *value);
			ret.clear();
			return false;
		}
		ret.push_back(fVal);
	}
	return true;
}

bool UIConfigResourceManager::getBoolList(UIModuleType moduleType, std::string blockName, std::string parameterName, std::vector<bool> &ret)
{
	std::string* value = getValue(moduleType, blockName, parameterName);
	if (value == 0)
		return false;
	std::vector<std::string> items;
	if (parseListValue(value, items) == false)
		return false;

	for (std::vector<std::string>::iterator it=items.begin(); it != items.end(); ++it) {
		if (equalsIgnoreCase(*it, "true")) {
			ret.push_back(true);
		}
		else if (equalsIgnoreCase(*it, "false")) {
			ret.push_back(false);
		}
		else {
			UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid boolean list: " + *value);
			ret.clear();
			return false;
		}
	}
	return true;
}

bool UIConfigResourceManager::getFileList(UIModuleType moduleType, std::string blockName, std::string parameterName, std::vector<std::string> &ret)
{
	std::string* value = getValue(moduleType, blockName, parameterName);
	if (value == 0)
		return false;

	std::vector<std::string> items;
	if (parseListValue(value, items) == false)
		return false;
	for (std::vector<std::string>::iterator it=items.begin(); it != items.end(); ++it)
		ret.push_back(*it);
	return true;
}

void UIConfigResourceManager::getAllParameterNames(UIModuleType moduleType, std::string blockName, std::vector<std::string> &paraNames)
{
	std::map<UIModuleType, UIConfig>::iterator itModule = m_configs.find(moduleType);
	if (itModule == m_configs.end()) {
		UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Fail to find configurations for module " + getModuleName(moduleType));
		return;
	}
	itModule->second.getAllParameters(blockName, paraNames);
}

bool UIConfigResourceManager::parseListValue(std::string* value, std::vector<std::string> &items)
{
	if (!startsWith(*value, "{") || !endsWith(*value, "}")) {
		UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Invalid list: " + *value);
		return false;
	}
	std::string val = *value;
	splitString(val.substr(1, value->length()-2), ",", items);
	trimAll(items);
	return true;
}

std::string *UIConfigResourceManager::getValue(UIModuleType moduleType, std::string blockName, std::string parameterName)
{
	std::map<UIModuleType, UIConfig>::iterator itModule = m_configs.find(moduleType);
	if (itModule == m_configs.end()) {
		UILogger::getInstance()->err(MODULE_OTHER, "UIConfigResourceManager", "Fail to find configurations for module " + getModuleName(moduleType));
		return 0;
	}
	return itModule->second.getValue(blockName, parameterName);
}

bool UIConfigResourceManager::isLocalModule(UIModuleType moduleType)
{
	for (std::vector<UIModuleType>::iterator itModule = m_localModules.begin(); itModule != m_localModules.end(); ++itModule) {
		if (*itModule == moduleType)
			return true;
	}
	return false;
}

bool UIConfigResourceManager::isLocalSocketServer(void)
{
	return equalsIgnoreCase(m_socketServerAddress, MODULE_ADDRESS_LOCAL);
}
